import datetime
from rest_framework import serializers
from .models import *
from datetime import timedelta
from schedule_task.scheduler import begin_exam, end_exam, scheduler
from rest_framework.exceptions import APIException
from django.db.models import F
from utils.handle_logger import logger


class SubjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = Subject
        fields = ['id', 'name', 'time', 'title_count', 'paper_count']
        extra_kwargs = {
            'id': {'read_only': True},
            'time': {'read_only': True},
            'title_count': {'read_only': True},
            'paper_count': {'read_only': True},
        }


class OptionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Option
        fields = ['id', 'prefix', 'content']


class TitleSerializer(serializers.ModelSerializer):
    optionItem = OptionSerializer(many=True, read_only=True)
    options = serializers.ListField(write_only=True)

    class Meta:
        model = Title
        fields = ['id', 'theme', 'right_answer', 'title_type', 'title_score', 'subject_name', 'type_name',
                  'time', 'blank', 'optionItem', 'subject', 'options']
        extra_kwargs = {
            'type_name': {'read_only': True},
            'blank': {'read_only': True},
            'subject': {'write_only': True},
            'id': {'read_only': True},
            'options': {'write_only': True},
        }

    def validate(self, attrs):
        title_type = attrs.get('title_type')
        title_id = self.context.get('title_id')
        options = attrs.pop('options')
        if title_type == 3:  # 简答题
            if title_id:  # 修改
                self._update_short_answer(title_id, attrs)
            else:  # 新增
                self._create_short_answer(attrs)
        if title_type == 2 or title_type == 1 or title_type == 0:  # 选择题
            if title_id:  # 修改
                self._update_choice_title(title_id, options, attrs)
            else:  # 新增
                self._create_choice_title(options, attrs)
        return attrs

    def _create_short_answer(self, attrs):
        title = Title.objects.create(**attrs)
        return title

    def _update_short_answer(self, title_id, attrs):
        title = Title.objects.filter(pk=title_id).update(**attrs)
        return title

    def _create_choice_title(self, options, attrs):
        title = Title.objects.create(**attrs)
        for option in options:
            option['option'] = title
            option.pop('id')
            Option.objects.create(**option)
        return title

    def _update_choice_title(self, title_id, options, attrs):
        title = Title.objects.filter(pk=title_id).update(**attrs)
        for option in options:
            option_id = option.pop('id')
            if option_id:  # 修改选项
                Option.objects.filter(pk=option_id).update(**option)
            else:  # 新增选项
                Option.objects.create(**option)
        return title


class TopicSerializer(serializers.ModelSerializer):
    titles = TitleSerializer(many=True)

    class Meta:
        model = Topic
        fields = ['content', 'titles']


class BlankPaperSerializer(serializers.ModelSerializer):
    topicItem = TopicSerializer(many=True, read_only=True)
    topics = serializers.ListField(write_only=True)

    class Meta:
        model = Paper
        fields = ['id', 'name', 'paper_score', 'time', 'subject', 'subject_name', 'topicItem', 'topics']
        extra_kwargs = {
            'id': {'read_only': True},
            'paper_score': {'read_only': True},
            'subject_name': {'read_only': True},
            'topicItem': {'read_only': True},
            'topics': {'write_only': True},
        }

    def validate(self, attrs):
        paper_id = self.context.get('paper_id')
        paper_score = self._get_score(attrs)
        if not paper_id:  # 新增试卷
            paper = Paper.objects.create(name=attrs.pop('name'), subject=attrs.pop('subject'), paper_score=paper_score)
            self._create_topic(paper, attrs)
        else:
            paper = Paper.objects.filter(pk=paper_id)
            paper.update(name=attrs.pop('name'), subject=attrs.pop('subject'),
                         paper_score=paper_score)
            # 先删除大标题 再新增
            topics = paper.first().topicItem.all()
            for topic in topics:
                topic.titles.clear()
                topic.delete()
            self._create_topic(paper.first(), attrs)
        return attrs

    def _create_topic(self, paper, attrs):
        topics = attrs.get('topics')
        for topic in topics:
            title_list = []
            for title in topic.get('titles'):
                title_list.append(title.get('id'))
            Topic.objects.create(content=topic.get('content'), paper=paper).titles.add(*title_list)
        return

    def _get_score(self, attrs):
        topics = attrs.get('topics')
        total_score = 0.00
        for topic in topics:
            for title in topic.get('titles'):
                title_score = title.get('title_score')
                total_score += float(title_score)
        return total_score


class StudentPaperSerializer(serializers.ModelSerializer):
    paper = BlankPaperSerializer()

    class Meta:
        model = StudentPaper
        fields = ['id', 'student_name', 'task_type', 'type_name', 'begin_time', 'deadline', 'task_duration', 'duration',
                  'status', 'sys_score', 'hand_score', 'final_score', 'paper']


class SimpleStudentPaperSerializer(serializers.ModelSerializer):
    class Meta:
        model = StudentPaper
        fields = ['id', 'task_name', 'task_type', 'type_name', 'student_name', 'duration', 'show_duration', 'status',
                  'show_status', 'sys_score', 'hand_score', 'final_score', 'begin_time', 'deadline']


class TaskSerializer(serializers.ModelSerializer):
    paper_dict = serializers.DictField(write_only=True)
    studentOfTask = SimpleStudentPaperSerializer(many=True, read_only=True)

    class Meta:
        model = Task
        fields = ['id', 'name', 'task_type', 'start_time', 'duration', 'paper', 'to_class', 'class_name', 'paper_dict',
                  'show_type', 'begin_time', 'studentOfTask']
        extra_kwargs = {
            'id': {'read_only': True},
            'show_type': {'read_only': True},
            'class_name': {'read_only': True},
            'studentOfTask': {'read_only': True},
            'paper': {'read_only': True},
            'begin_time': {'read_only': True},
            'paper_dict': {'write_only': True},
        }

    def validate(self, attrs):
        task_id = self.context.get('task_id')
        class_obj = attrs.get('to_class')
        paper = attrs.pop('paper_dict')
        student_list = class_obj.studentClass.all()
        task_type = attrs.get('task_type')
        if not task_id:  # 新增考试任务
            self._create_task(attrs, paper, student_list)
        else:
            self.del_task(task_id, task_type)
            self._create_task(attrs, paper, student_list)
        return attrs

    def del_task(self, task_id, task_type):
        try:
            scheduler.remove_job(str(task_id))
            if task_type == 0:
                scheduler.remove_job(f"{str(task_id)}_end")
        except Exception as e:
            logger.error(f"删除定时任务异常: {e}")
        Task.objects.filter(pk=task_id).delete()

    def _begin_task(self, task, attrs, deadline):
        begin_time = attrs.get('start_time')
        scheduler.add_job(begin_exam, 'date', run_date=str(begin_time), id=str(task.id), args=[task])
        if task.task_type == 0:
            scheduler.add_job(end_exam, 'date', run_date=str(deadline), id=f"{str(task.id)}_end", args=[task])
        try:
            scheduler.start()
        except Exception as e:
            logger.error(f"开启Scheduler异常: {e}")

    def _create_task(self, attrs, paper, student_list):
        attrs['start_time'] += timedelta(hours=8)
        start_time = attrs.get('start_time')
        delta_time = start_time - datetime.datetime.now()
        if str(delta_time).startswith('-'):
            raise APIException('考试开始时间已过,请重新选择')
        attrs['paper_id'] = paper.get('id')
        task = Task.objects.create(**attrs)
        duration = attrs.pop('duration')
        deadline = start_time + timedelta(minutes=int(duration) + 1)
        attrs.pop('name')
        attrs.pop('to_class')
        attrs['task'] = task
        self._create_student_paper(student_list, attrs, paper)
        self._begin_task(task, attrs, deadline)

    def _create_student_paper(self, student_list, attrs, paper):
        for student in student_list:
            attrs['student'] = student
            student_paper = StudentPaper.objects.create(**attrs)
            for topic in paper.get('topicItem'):
                for title in topic.get('titles'):
                    title_id = title.get('id')
                    StudentAnswer.objects.create(student_paper=student_paper, title_id=title_id)


class StudentAnswerSerializer(serializers.ModelSerializer):
    paper = serializers.DictField(write_only=True)
    sys_score = serializers.FloatField(write_only=True)

    class Meta:
        model = StudentAnswer
        fields = ['id', 'paper', 'sys_score', 'content', 'student_paper', 'title', 'student_score', 'is_right',
                  'right_type', ]

        extra_kwargs = {
            'id': {'read_only': True},
            'content': {'read_only': True},
            'student_paper': {'read_only': True},
            'title': {'read_only': True},
            'student_score': {'read_only': True},
            'is_right': {'read_only': True},
            'right_type': {'read_only': True},
            'paper': {'write_only': True},
            'sys_score': {'write_only': True},
        }

    def validate(self, attrs):
        viewer = self.context.get('viewer')
        student_paper = self.context.get('student_paper')
        if viewer.action == 'submit_paper':
            self._auto_check_paper(attrs, student_paper)
        elif viewer.action == 'check_paper':
            self._hand_check_paper(attrs, student_paper)
        return attrs

    def _auto_check_paper(self, attrs, student_paper):
        topics = attrs.get('paper').get('topicItem')
        sys_score = 0.00
        for topic in topics:
            for title in topic.get('titles'):
                student_score = float(title.get('title_score'))
                title_type = title.get('title_type')
                answer_id = title.get('answer').get('id')
                content = title.get('blank')
                right_answer = title.get('right_answer')
                if title_type == 3:  # 简答题
                    StudentAnswer.objects.filter(pk=answer_id).update(content=content)
                elif title_type == 0 or title_type == 2:
                    if content == right_answer:  # 单选/判断题正确
                        StudentAnswer.objects.filter(pk=answer_id).update(content=content, student_score=student_score,
                                                                          is_right=1)
                        sys_score += student_score
                    else:  # 单选题错误
                        StudentAnswer.objects.filter(pk=answer_id).update(content=content, student_score=0.00,
                                                                          is_right=3)
                elif title_type == 1:  # 多选题评分
                    multi_score = self._check_multi_choice(student_score, content, right_answer, answer_id)
                    sys_score += multi_score
        StudentPaper.objects.filter(pk=student_paper.id).update(sys_score=sys_score)

    def _check_multi_choice(self, student_score, content, right_answer, answer_id):
        res = ""
        for i in content:
            res += i
        if len(res) <= 0:
            StudentAnswer.objects.filter(pk=answer_id).update(content=res, is_right=3, student_score=0.00)
            return 0.00
        if len(res) > len(right_answer):
            StudentAnswer.objects.filter(pk=answer_id).update(content=res, is_right=3, student_score=0.00)
            return 0.00
        elif len(res) <= len(right_answer):
            for choice in res:
                if choice not in right_answer:
                    StudentAnswer.objects.filter(pk=answer_id).update(content=res, is_right=3, student_score=0.00)
                    return 0.00
            else:
                if len(res) == len(right_answer):
                    StudentAnswer.objects.filter(pk=answer_id).update(content=res, is_right=1,
                                                                      student_score=student_score)
                    return student_score
                else:
                    multi_score = student_score / 2
                    StudentAnswer.objects.filter(pk=answer_id).update(content=res, is_right=2,
                                                                      student_score=multi_score)
                    return multi_score

    def _hand_check_paper(self, attrs, student_paper):
        topics = attrs.get('paper').get('topicItem')
        hand_score = 0.00
        for topic in topics:
            for title in topic.get('titles'):
                is_right = 1
                if title.get('title_type') == 3:
                    student_score = float(title.get('answer').get('student_score'))
                    title_score = float(title.get('title_score'))
                    if student_score > title_score:
                        student_score = title_score
                    hand_score += student_score
                    if 0 < student_score < title_score:
                        is_right = 2
                    elif not student_score:
                        is_right = 3
                    StudentAnswer.objects.filter(pk=title.get('answer').get('id')).update(student_score=student_score,
                                                                                          is_right=is_right)
                else:
                    continue
        StudentPaper.objects.filter(pk=student_paper.id).update(hand_score=hand_score,
                                                                final_score=F('sys_score') + hand_score, status=3)
